/*=============================================================================
  CREMeshImpl.cpp : implementation of OcLeaf Render Element.
  Copyright (c) 2001 Crytek Studios. All Rights Reserved.

  Revision history:
    * Created by Honitch Andrey

=============================================================================*/

#include "StdAfx.h"
#include <ICryAnimation.h>


void CREMeshImpl::mfReset()
{
}

void CREMeshImpl::mfCenter(Vec3& Pos, CRenderObject*pObj)
{
  Vec3 Mins = m_pRenderMesh->m_vBoxMin;
  Vec3 Maxs = m_pRenderMesh->m_vBoxMax;
  Pos = (Mins + Maxs) * 0.5f;
  if (pObj)
    Pos += pObj->GetTranslation();
}

void CREMeshImpl::mfGetBBox(Vec3& vMins, Vec3& vMaxs)
{
  vMins = m_pRenderMesh->_GetVertexContainer()->m_vBoxMin;
  vMaxs = m_pRenderMesh->_GetVertexContainer()->m_vBoxMax;
}

float CREMeshImpl::mfDistanceToCameraSquared(Matrix34& matInst)
{
  CRenderer *rd = gRenDev;

  Vec3 Mins = m_pRenderMesh->_GetVertexContainer()->m_vBoxMin;
  Vec3 Maxs = m_pRenderMesh->_GetVertexContainer()->m_vBoxMax;
  //Vec3 Center = (Mins + Maxs) * 0.5f;
  //Center += thisObject.GetTranslation();
  //Vec3 delta = rd->GetRCamera().Orig - Center;
  AABB aabb(Mins, Maxs);
  float fDistSq = Distance::Point_AABBSq(rd->GetRCamera().Orig-matInst.GetTranslation(), aabb);
  return fDistSq;

/*	float dist = delta.GetLength();
	dist += 60.f;//TODO: remove me, i am a hack for cxp to let the particle effect be in front of the sphere surrounding it
	return sqr(dist);
*/
  //return (delta).GetLengthSquared();
}

///////////////////////////////////////////////////////////////////

void CREMeshImpl::mfPrepare(bool bCheckOverflow)
{
  CRenderer *rd = gRenDev;

  if (bCheckOverflow)
    rd->FX_CheckOverflow(0, 0, this);

	rd->m_RP.m_CurVFormat = m_pRenderMesh->_GetVertexFormat();

  {
    //PROFILE_FRAME_TOTAL(Mesh_REPrepare_Ocleaf);
    rd->m_RP.m_pRE = this;

		rd->m_RP.m_FirstVertex = m_nFirstVertId;
		rd->m_RP.m_FirstIndex =  m_nFirstIndexId;
		rd->m_RP.m_RendNumIndices = m_nNumIndices;
		rd->m_RP.m_RendNumVerts = m_nNumVerts;
  }
}

PodArray<CRenderChunk> *CREMeshImpl::mfGetMatInfoList()
{
  return &m_pRenderMesh->m_Chunks;
}

int CREMeshImpl::mfGetMatId()
{
  return m_pChunk->m_nMatID;
}

CRenderChunk *CREMeshImpl::mfGetMatInfo()
{
  return m_pChunk;
}

void CREMeshImpl::mfPrecache(const SShaderItem& SH)
{
  CShader *pSH = (CShader *)SH.m_pShader;
  if (!pSH)
    return;
  if (!m_pRenderMesh)
    return;
  if (m_pRenderMesh->_HasVBStream(VSF_GENERAL))
    return;

  mfCheckUpdate(pSH->m_eVertexFormat, VSM_TANGENTS, gRenDev->m_RP.m_TI[gRenDev->m_RP.m_nFillThreadID].m_nFrameUpdateID);
}

bool CREMeshImpl::mfUpdate(EVertexFormat eVertFormat, int Flags, bool bTessellation)
{
	FUNCTION_PROFILER_RENDER_FLAT
  //PROFILE_FRAME(Mesh_CheckUpdate);
  CRenderer *rd = gRenDev;
	const int threadId = rd->m_RP.m_nProcessThreadID;

  bool bSucceed = true;
	
	CRenderMesh *pVContainer = m_pRenderMesh->_GetVertexContainer();

  if (rd->m_RP.m_pShader && (rd->m_RP.m_pShader->m_Flags2 & EF2_DEFAULTVERTEXFORMAT))
    eVertFormat = pVContainer->_GetVertexFormat();
	  
	m_pRenderMesh->m_nFlags &= ~FRM_SKINNEDNEXTDRAW;

	if (m_pRenderMesh->m_Modified[threadId].linked())
	{
		m_pRenderMesh->SyncAsyncUpdate(gRenDev->m_RP.m_nProcessThreadID); 

		bSucceed = m_pRenderMesh->RT_CheckUpdate(pVContainer, eVertFormat, Flags | VSM_MASK, bTessellation);
	}

	if (!bSucceed || !pVContainer->_HasVBStream(VSF_GENERAL))
    return false;

	bool bSkinned = (m_pRenderMesh->m_nFlags & (FRM_SKINNED | FRM_SKINNEDNEXTDRAW)) != 0;










	if ((Flags | VSM_MASK) & VSM_TANGENTS)
	{
		if (bSkinned && pVContainer->_HasVBStream(VSF_QTANGENTS))
		{
			rd->m_RP.m_FlagsStreams_Stream &= ~VSM_TANGENTS;
			rd->m_RP.m_FlagsStreams_Decl &= ~VSM_TANGENTS;
			rd->m_RP.m_FlagsStreams_Stream |= (1<<VSF_QTANGENTS);
			rd->m_RP.m_FlagsStreams_Decl |= (1<<VSF_QTANGENTS);
		}
	}







  rd->m_RP.m_CurVFormat = pVContainer->_GetVertexFormat();
  m_Flags &= ~FCEF_DIRTY;

  return true;
}

void *CREMeshImpl::mfGetPointer(ESrcPointer ePT, int *Stride, EParamType Type, ESrcPointer Dst, int Flags)
{
  CRenderMesh *pRM = m_pRenderMesh->_GetVertexContainer();
  byte *pD = NULL;
	IRenderMesh::ThreadAccessLock lock(pRM); 

  switch(ePT) 
  {
    case eSrcPointer_Vert:
      pD = pRM->GetPosPtr(*Stride,  FSL_READ);
      break;
    case eSrcPointer_Tex:
      pD = pRM->GetUVPtr(*Stride,  FSL_READ);
      break;
    case eSrcPointer_Normal:
      pD = pRM->GetNormPtr(*Stride,  FSL_READ);
      break;
    case eSrcPointer_Binormal:
      pD = pRM->GetBinormalPtr(*Stride, FSL_READ);
      break;
    case eSrcPointer_Tangent:
      pD = pRM->GetTangentPtr(*Stride, FSL_READ);
      break;
    case eSrcPointer_Color:
      pD = pRM->GetColorPtr(*Stride, FSL_READ);
      break;
    default:
      assert(false);
      break;
  }
  if (m_nFirstVertId && pD)
    pD += m_nFirstVertId * (*Stride);
  return pD;
}

void CREMeshImpl::mfGetPlane(Plane& pl)
{
  CRenderMesh *pRM = m_pRenderMesh->_GetVertexContainer();

  // fixme: plane orientation based on biggest bbox axis
  Vec3 pMin, pMax;
  mfGetBBox(pMin, pMax);
  Vec3 p0 = pMin;
  Vec3 p1 = Vec3(pMax.x, pMin.y, pMin.z);
  Vec3 p2 = Vec3(pMin.x, pMax.y, pMin.z);
  pl.SetPlane(p2, p0, p1);
}
